using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace ModelGenerator.RepositoryGenerator.Builders
{
    public class ARRepositoryBuilder
    {
        #region Properties

        private readonly CodeCompileUnit classCodeBase;
        private readonly CodeCompileUnit interfaceCodeBase;
        public string SourceNameSpace { get; set; }
        public string TargetNameSpace { get; set; }
        public string DomainClassName { get; set; }
        public string BaseType { get; set; }
        public string BaseTypeNameSpace { get; set; }
        public IList<PropertyInfo> MemberType { get; set; }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ARRepositoryBuilder"/> class.
        /// </summary>
        public ARRepositoryBuilder()
        {
            classCodeBase = new CodeCompileUnit();
            interfaceCodeBase = new CodeCompileUnit();
        }

        #endregion

        /// <summary>
        /// Creates the files.
        /// </summary>
        /// <param name="targetDir">The target dir.</param>
        public void CreateFiles(string targetDir)
        {
            CreateFile(string.Format(@"{0}\{1}Repository.cs", targetDir, DomainClassName), classCodeBase);
            CreateFile(string.Format(@"{0}\I{1}Repository.cs", targetDir, DomainClassName), interfaceCodeBase);
        }

        /// <summary>
        /// Creates the file.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="compileUnit">The compile unit.</param>
        private static void CreateFile(string path, CodeCompileUnit compileUnit)
        {
            if (File.Exists(path))
                File.Delete(path);

            var options = new CodeGeneratorOptions {VerbatimOrder = true, BracingStyle = "C"};

            var sw = new IndentedTextWriter(new StreamWriter(path), "\t");
            CodeDomProvider.CreateProvider("cs").GenerateCodeFromCompileUnit(compileUnit, sw, options);
            sw.Close();
        }

        /// <summary>
        /// Creates the AR repository.
        /// </summary>
        public void CreateARRepository()
        {
            CreateNameSpace();
            AddUsing(classCodeBase, false);
            AddUsing(interfaceCodeBase, true);
            CreateClass();
            CreateInterface();
            AddFunctions();
        }

        #region RepositoryClassGeneration

        /// <summary>
        /// Adds the functions.
        /// </summary>
        private void AddFunctions()
        {
            foreach (var info in MemberType)
            {
                FunctionBuilder.CreateFunctions(classCodeBase, interfaceCodeBase, info, DomainClassName);
            }
        }

        /// <summary>
        /// Creates the interface.
        /// </summary>
        private void CreateInterface()
        {
            var RepositoryInterface = new CodeTypeDeclaration(string.Format("I{0}Repository", DomainClassName));
            RepositoryInterface.IsInterface = true;
            RepositoryInterface.IsPartial = true;
            RepositoryInterface.Attributes = MemberAttributes.Public;
            if (!string.IsNullOrEmpty(BaseTypeNameSpace))
                RepositoryInterface.BaseTypes.Add(
                    new CodeTypeReference(string.Format("IActiveRecordRepository<{0}>", DomainClassName)));

            interfaceCodeBase.Namespaces[0].Types.Add(RepositoryInterface);
        }

        /// <summary>
        /// Creates the class.
        /// </summary>
        private void CreateClass()
        {
            var RepositoryClass = new CodeTypeDeclaration(string.Format("{0}Repository", DomainClassName));
            RepositoryClass.IsClass = true;
            RepositoryClass.IsPartial = true;
            RepositoryClass.Attributes = MemberAttributes.Public;
            if (!string.IsNullOrEmpty(BaseTypeNameSpace))
                RepositoryClass.BaseTypes.Add(new CodeTypeReference(BaseType, new CodeTypeReference(DomainClassName)));
            RepositoryClass.BaseTypes.Add(new CodeTypeReference(string.Format("I{0}Repository", DomainClassName)));

            classCodeBase.Namespaces[0].Types.Add(RepositoryClass);
        }

        /// <summary>
        /// Adds the using.
        /// </summary>
        private void AddUsing(CodeCompileUnit compileUnit, bool isInterface)
        {
            compileUnit.Namespaces[0].Imports.Add(new CodeNamespaceImport("System"));
            compileUnit.Namespaces[0].Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
            compileUnit.Namespaces[0].Imports.Add(new CodeNamespaceImport(SourceNameSpace));
            if (!string.IsNullOrEmpty(BaseTypeNameSpace) && TargetNameSpace != BaseTypeNameSpace)
                compileUnit.Namespaces[0].Imports.Add(new CodeNamespaceImport(BaseTypeNameSpace));

            if (!isInterface)
                compileUnit.Namespaces[0].Imports.Add(new CodeNamespaceImport("NHibernate.Expression"));
        }

        /// <summary>
        /// Creates the name space.
        /// </summary>
        private void CreateNameSpace()
        {
            classCodeBase.Namespaces.Add(new CodeNamespace(TargetNameSpace));
            interfaceCodeBase.Namespaces.Add(new CodeNamespace(TargetNameSpace));
        }

        #endregion
    }
}